package at.jku.analyzer.modules;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import at.jku.analyzer.interfaces.AnalyzerModule;

public class JavaAnalyzer extends AnalyzerModule {
	
	private static final Map<String, Integer> MAP_OF_KEYWORDS = new HashMap<>();
	
	private static final List<String> KEYWORDS = Arrays.asList("abstract","continue", "for", "new", "switch", "assert", 
			"default", "goto", "package", "synchronized", "boolean", "do", "if", "private", "this", "break", "double", 
			"implements", "protected", "throw", "byte", "else", "import", "public", "throws", "case", "default", "enum", 
			"instanceof", "return", "transient", "catch", "extends", "int", "short", "try", "char", "final", 
			"interface", "static", "void", "class", "finally", "long", "strictfp", "volatile", "const", "float", "native", 
			"super", "while");
	
	public JavaAnalyzer(File file) throws IOException {
		super(file);
		
		for (String s : KEYWORDS) {
			MAP_OF_KEYWORDS.put(s, 0);
		}
	}
	
	/**
	 * Each comment will increase the counter by one. While each row of the '//'
	 * comment increase the counter by one, the multi-row comment which starts
	 * with the characters '/*' and end stops if '* /' occurs will also increase
	 * by one no matter how much rows are between these two points.
	 */
	@Override
	public int getLinesOfComments() {
		resetReader();
		// the number of comments in this file
		int counter = 0;
		
		try {
			// read the first character
			char c = nextCh();
			
			// analyze till the end of file is reached
			while (c != EOF) {
				// the comments starts always if '/' occurs
				if (c == '/') {
					c = nextCh();
					
					// one line comment; after two '/' characters, the counter
					// will increase by one and skip the current characters in
					// this row
					if (c == '/') {
						c = nextCh();
						skipLineComment(c);
						counter++;
					}
					
					// multirow comment, starts at '/*' and end if '*/' occurs
					if (c == '*') {
						// is true if the comment close successfully
						// boolean endOfComment = false;
						c = nextCh();
						
						if (skipMultiLineComment(c)) {
							counter++;
						}
					}
				}
				
				c = nextCh();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		return counter;
	}
	
	@Override
	public Map<String, Integer> getNumOfKeywords() {
		resetReader();
		
		try {
			char c = nextCh();
			StringBuilder str = new StringBuilder();
			
			while (c != EOF) {
				if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
					str.append(c);
				} else if (c == '/') {
					c = nextCh();
					if (c == '/') {
						skipLineComment(nextCh());
						str.setLength(0);
					} else if (c == '*') {
						skipMultiLineComment(nextCh());
						str.setLength(0);
					}
				} else {
					updateKeyList(str.toString(), MAP_OF_KEYWORDS);
					str.setLength(0);
				}
				
				c = nextCh();
			}
			
			updateKeyList(str.toString(), MAP_OF_KEYWORDS);
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		// remove all keywords entries with count = 0
		Iterator<Entry<String, Integer>> it = MAP_OF_KEYWORDS.entrySet().iterator();
		
		while (it.hasNext()) {
			if (it.next().getValue().equals(0)) {
				it.remove();
			}
		}
		
		return MAP_OF_KEYWORDS;
	}
	
	private void skipLineComment(char c) throws IOException {
		while (c != LF && c != CR && c != EOF) {
			c = nextCh();
		}
	}
	
	private boolean skipMultiLineComment(char c) {
		boolean endofComment = false;
		char lastCh = c;
		c = nextCh();
		
		while (c != EOF && !endofComment) {
			if (lastCh == '*' && c == '/') {
				endofComment = true;
				break;
			}
			
			lastCh = c;
			c = nextCh();
		}
		
		return endofComment;
	}
	
	private void updateKeyList(String str, Map<String, Integer> list) {
		if (list.containsKey(str)) {
			int val = list.get(str);
			list.put(str, ++val);
		}
	}
	
}